Repository navigation
DIRMINA-1199 - race condition when sending multiple larger data chunks - #77
Conversation
|
I have applied your patch, and tested it with Thomas's test: it's still failing. The synchronized queue (for instance mReceiveQueue) must also be synchronized where it is used (like in the receive_loop method) in all the class, otherwise it will not be very useful (FTR, I have added such synchronized section everywhere the mReceiveQueue and mWritingQueue are used, it does not help |
|
Interesting. Let me check. |
|
Are you sure about this? I just run the test attached to JIRA 20 times and it didn't fail. Test must throw exception to fail, error logs are just for logging and not changing log4j config. I can merge Thomas's test in this PR. Should I? |
|
Ah my bad, I applied the patch in a MINA code base that is not the one that contains the test... Let me reapply it at the right place and restest it. |
42cac50 to
51510e4
Compare
|
Ok, once applied, Thomas' test is green. (actually, adding the synchronized on mWriteQueue is enough). Still this is mysterious why this synchronized bit is necessary. The SSL Filter will be executed on one single thread, where the second thread accessing this queue is coming from? |
|
A test for the fix should also be bidirectional: the server should send back some reply on a message reception and the client should read that reply. |
I will add this. Probably not today.
The logging for forward received is put in the bad spot. I will improve this. |
|
The two SslHandler instances are different instances (@5b867368 and @61e56151). So the Synchronized(mWriteQueue) 'fixes' the issue but I don't understand how... |
|
I had Claude look into it and provided some background. Root cause / history The
They were commented out in 62643fe ("alright lets remove all the synchronization from the queue flushes"). That was an experiment on the in-progress Why Encryption happens under the handler monitor, so records go into
If two threads poll consecutive records and reach
Requested changes
+1 on the fix itself once the tests are cleaned up. |
|
Hi Jon, long time no see ;-) I buy what Claude says. Still I'm having hard time understanding how 2 different threads could concurrently access the same queue when each thread uses a different SslHandler instance, each one with its own instance of a queue, so there is no way one queue can be shared between those two threads... |
You are right and now I am confused. I did some grepping and I don't see them interleaving in an obvious way. I will look tomorrow.
After making the test bidirectional as suggested by Thomas - the acceptor sends the response back - the synchronization on I removed Pushed latest changes with additional logging outside of synchronized blocks. |
|
That is an interesting bug hunt ;-) |
Which queue are you referring to? The queues are per connection/socket. Going off memory here... In HTTP/2 for example, the chunking + virtual channel mechanism could span multiple async requests in parallel on different threads with each thread submitting chunks/files into writer pipeline for a single socket. Mina does not guarantee any kind of ordering on the write pipeline and leaves it entirely up to your own implementation. The synchronized monitors around the queues are necessary not because the queue is unsafe but because the pipeline function of polling from the queue and pushing into the connection/socket needs to be synchronized. To prevent write corruption, the order encrypted messages are pulled from the queue and written to the socket must be guaranteed. |
4875580 to
ca25c0c
Compare
|
Claude seems to be right. Write is the problem on the sender side. Processor competes with jUnit thread. |
|
@the-thing check my original PR #44 and the original commit before the experiment. I explained the reason for this design and drew a diagram in the description. |
|
I had a look at the PR and it makes sense to me, but I clearly do not understand all the pieces. I'm not questioning the design, but I was more curious about the fact that at some point you decided to remove synchronized blocks from the code, but they were initially put in place, because you had a glimpse of failing scenarios.
So the bottom line is that this particular commit should have stayed. |
It should have been reverted. It was an experiment because I was on the email thread with someone who wanted to run tests with some harness they were unable to share with me. The literal |
ca25c0c to
a670604
Compare
tomaswolf
left a comment
There was a problem hiding this comment.
Nice test. Some inline comments below.
The test runs quite long on my machine (some 80 seconds).
The mWriteQueue.
Totally agree on this. My concern is for this specific test that demonstrates the issue: we have one connector sending a big message to one acceptor, and wait for the response. And still, we have two threads messing with the queue, which I don't understand.
Agreed. |
I'm probably the one who merged the change into trunk by mistake. The mail, thread was off the dev mailing list, between Jon, me and two other people, and it was related to https://issues.apache.org/jira/browse/DIRMINA-1173. @jon-valliere you wrote that: "I’m still a little concerned about the additions I made. It is very much an experimental version which has to be enabled manually. The last commit I made makes it even more so. The original version, IMHO is very stable and safely designed." and that was pretty much the last messages before your change gets merged into trunk by me in April, 18th 2024, pretty much 2 months after the mail discussion (and I most certainly merged for some other reason, having forgotten what it was all about). |
|
At this point, I think we should revert the change, have the queues synchronized as it was before. Still, I'm having hard time understanding how it solves the issue and have the test passing. I'll have a 4 hours train trip this morning, I'll try to check the ins and outs of the issue, I'm very confused at this point... Thanks @the-thing @tomaswolf and @jon-valliere for all your inputs! |
|
Latest changes pushed as suggested. With the current setup the build is significantly longer especially on Linux (these environment seem to have some problems in recent days as the delay to start seem to be longer) https://github.com/apache/mina/actions/runs/37891283765 Test JDK 17, ubuntu-latest succeeded 2 minutes ago in 15m 10s Test JDK 17, windows-latest succeeded 13 minutes ago in 4m 49s Test JDK 17, macos-latest succeeded 12 minutes ago in 5m 11s |
tomaswolf
left a comment
There was a problem hiding this comment.
Thanks for the updates; some findings below.
|
I just ran this test on Windows with the old SSLHandlerG1 (without the synchronization fixes), and it succeeded. The test should fail under these conditions. Moreover, when run on a JDK 8.0.422.5-hotspot, it fails to connect with TLS 1.2 and TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256. With TLS 1.2 and TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 is can connect. |
And it occasionally will succeed. The maximize failure we have to significantly increase iteration count (or for example enabling DEBUG logging helps). I'm not sure if we should increase the count since the build time increased significantly already.
I'm assuming this is due to the fact ciphers differ between Java version. Do we want to find a cipher that works for all possible versions (possible?) or just those that are CI configured? |
The time it takes on the GH's CI/CD is not relevant. You can't tell if the VM were started on a heavily loaded server, the build might be faster a few hours later. If you run the build on your computer and sees a increase, than that would be relevant... And yes, as there is a synchronized section, it will be slower with the tests we have. |
Yes, that's the problem with race conditions. I have no idea how we could provoke hitting it more frequently.
As part of the release process we will run tests locally, and they must not fail. So yes, we have to find a way to make this work on CI and locally in different configurations. I see two ways:
|
Mentally I was insisting on using a single cipher suite for each test, but probably it might be better just to provide a list of cipher suite to avoid conditions (junit assumptions as suggested). Cipher suites are not the issue for this problem (but might have impact in the future). I was thinking of changing the parameters to: BTW. Is the Java 8 build even supported? I tried to build it locally using JDK 8 with |
No, we must use Java 17 to build MINA 2.2.X branch: We have also updated the web site some time ago to be clear about it: https://mina.apache.org/mina-project/developer-guide.html#checking-out-the-code However we produce code that is Java 8 compliant: The pom.xml should be updated to use: accordingly to https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-release.html |
There was a problem hiding this comment.
All seems ok to me.
I have more questions, but at this point I suggest we start a thread on the mailing list: it's easier to find it when needed two years later, instead of having to check many JIRA tickets.
And I'd like to discuss when we should cut a release containing this test and this fix: again, it will be on the mailing list.
Many thanks @the-thing, @tomaswolf and @jon-valliere for the forensic analysis, and the correction!
|
Thanks for help and input. I'm glad it is overish... ;) |
It seems that multithreaded polling leads to message read interleaving problem ABA. Writes are synchronized, but reading from intermediate queues is not.
Looking at the history, the initial implementation was using poll queues locks, but they were removed half way. The area wasn't covered with tests and it got away.